package com.bluesea.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import com.bluesea.common.Page;
import com.bluesea.dao.GenericDao;


@SuppressWarnings("unchecked")
public class GenericDaoJpa<T, PK extends Serializable> implements
		GenericDao<T, PK> {

	private Class<T> persistentClass;

	@PersistenceContext
	private EntityManager entityManager;

	public GenericDaoJpa() {
		Class c = getClass();
		Type t = c.getGenericSuperclass();
		if (t instanceof ParameterizedType) {
			Type[] p = ((ParameterizedType) t).getActualTypeArguments();
			this.persistentClass = (Class<T>) p[0];
		}
	}

	/**
	 * @return the entityManager
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * @param entityManager
	 *            the entityManager to set
	 */
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	public void create(T entity) {
		entityManager.persist(entity);
	}

	public T read(PK id) {
		return entityManager.find(this.persistentClass, id);
	}

	public void delete(T entity) {
		entityManager.remove(entity);
	}

	public void update(T entity) {
		entityManager.merge(entity);
	}

	public List<T> findAll() {
		return find("from " + persistentClass.getName()
				+ " t order by t.id desc");
	}

	public Page<T> findAll(int pageNumber, int pageSize) {
		String hql = "from " + persistentClass.getName()
				+ " t order by t.id desc";

		return pagedQuery(hql, pageNumber, pageSize);
	}

	protected Page<T> pagedQuery(String hql, int pageNumber,
			int pageSize, Object... values) {

		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List<T> countlist = find(countQueryString, values);
		Long totalCount = 0L;
		if (countlist.size() > 0)
			totalCount = (Long) countlist.get(0);

		if (totalCount < 1) {
			return new Page<T>();
		}

		int startIndex = Page.getStartRow(pageNumber, pageSize);
		Query query = createQuery(hql, values);
		query.setFirstResult(startIndex);
		query.setMaxResults(pageSize);
		query.setHint("org.hibernate.cacheable", true);
		List<T> list = query.getResultList();

		return new Page<T>(pageSize, totalCount.intValue(), list,
				pageNumber);
	}

	protected List<T> getTopLength(String hql, int length, Object... values) {
		Query query = createQuery(hql, values);
		query.setFirstResult(0);
		query.setMaxResults(length);
		query.setHint("org.hibernate.cacheable", true);
		List<T> list = query.getResultList();

		return list;
	}

	protected List<T> find(String queryString, Object... values) {
		Query qry = createQuery(queryString, values);
		qry.setHint("org.hibernate.cacheable", true);
		return qry.getResultList();
	}

	protected static String removeSelect(String hql) {
		int beginPos = hql.toLowerCase().indexOf("from");

		return hql.substring(beginPos);
	}

	protected static String removeOrders(String hql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);

		return sb.toString();
	}

	public Query createQuery(String hql, Object... values) {
		Query qry = entityManager.createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			qry.setParameter(i + 1, values[i]);
		}
		qry.setHint("org.hibernate.cacheable", true);
		return qry;
	}
}
